#ifndef _MW_LOG_STREAM_HPP_
#define _MW_LOG_STREAM_HPP_

#include "config.hpp"
#include "logger.hpp"
#include <sstream>

namespace mw {

class LogStream
{
	struct Stream {
		Stream(): ref(1), space(true) {}
		std::stringstream ss;
		int ref;
		bool space;
	} *stream;
	mw::LogType logType_;
public:
	inline LogStream(mw::LogType logType):stream(new Stream), logType_(logType) {}
	inline LogStream(const LogStream &o):stream(o.stream), logType_(o.logType_) { ++stream->ref; }
	inline ~LogStream() {
		if (!--stream->ref) {
			Logger::instance().log(stream->ss.str(), logType_);
			delete stream;
		}
	}
	inline LogStream &space() { stream->space = true; stream->ss << " "; return *this; }
	inline LogStream &nospace() { stream->space = false; return *this; }
	inline LogStream &maybe_space() { if (stream->space) stream->ss << " "; return *this; }

	inline LogStream &operator<<(char t) { stream->ss << "\'" << t << "\'"; return maybe_space(); }
	inline LogStream &operator<<(bool t) { stream->ss << (t ? "true" : "false"); return maybe_space(); }
	inline LogStream &operator<<(signed short t) { stream->ss << t; return maybe_space(); }
	inline LogStream &operator<<(unsigned short t) { stream->ss << t; return maybe_space(); }
	inline LogStream &operator<<(signed int t) { stream->ss << t; return maybe_space(); }
	inline LogStream &operator<<(unsigned int t) { stream->ss << t; return maybe_space(); }
	inline LogStream &operator<<(signed long t) { stream->ss << t; return maybe_space(); }
	inline LogStream &operator<<(unsigned long t) { stream->ss << t; return maybe_space(); }
	inline LogStream &operator<<(signed long long t)
	{ stream->ss << t; return maybe_space(); }
	inline LogStream &operator<<(unsigned long long t)
	{ stream->ss << t; return maybe_space(); }
	inline LogStream &operator<<(float t) { stream->ss << t; return maybe_space(); }
	inline LogStream &operator<<(double t) { stream->ss << t; return maybe_space(); }
	inline LogStream &operator<<(const char* t) { stream->ss << t; return maybe_space(); }
	inline LogStream &operator<<(const std::string & t) { stream->ss << "\"" << t  << "\""; return maybe_space(); }
	inline LogStream &operator<<(const void * t) { stream->ss << t; return maybe_space(); }
};

class NullLogStream
{
public:
	inline NullLogStream(mw::LogType logType) {}
	inline NullLogStream(const NullLogStream &o) {}
	inline ~NullLogStream() {}
	inline NullLogStream &space() { return *this; }
	inline NullLogStream &nospace() { return *this; }
	inline NullLogStream &maybe_space() { return *this; }

	inline NullLogStream &operator<<(char t) { return *this; }
	inline NullLogStream &operator<<(bool t) { return *this; }
	inline NullLogStream &operator<<(signed short t) { return *this; }
	inline NullLogStream &operator<<(unsigned short t) { return *this; }
	inline NullLogStream &operator<<(signed int t) { return *this; }
	inline NullLogStream &operator<<(unsigned int t) { return *this; }
	inline NullLogStream &operator<<(signed long t) { return *this; }
	inline NullLogStream &operator<<(unsigned long t) { return *this; }
	inline NullLogStream &operator<<(signed long long t)	{ return *this; }
	inline NullLogStream &operator<<(unsigned long long t) { return *this; }
	inline NullLogStream &operator<<(float t) { return *this; }
	inline NullLogStream &operator<<(double t) { return *this; }
	inline NullLogStream &operator<<(const char* t) { return *this; }
	inline NullLogStream &operator<<(const std::string & t) { return *this; }
	inline NullLogStream &operator<<(const void * t) { return *this; }
};


#ifdef _DEBUG
inline LogStream mtrace() { return LogStream(mw::TRACE); }
inline LogStream mdebug() { return LogStream(mw::DEBUG); }
#else
inline NullLogStream mtrace() { return NullLogStream(mw::TRACE); }
inline NullLogStream mdebug() { return NullLogStream(mw::DEBUG); }
#endif // _DEBUG
inline LogStream minfo() { return LogStream(mw::INFO); }
inline LogStream mwarning() { return LogStream(mw::WARNING); }
inline LogStream merror() { return LogStream(mw::ERROR); }
inline LogStream mcritical() { return LogStream(mw::CRITICAL); }

} // end namespace mw

#ifndef MW_DONOT_EXPORT_DEBUG_API
using mw::mtrace;
using mw::mdebug;
using mw::minfo;
using mw::mwarning;
using mw::merror;
using mw::mcritical;
#endif // MW_DONOT_EXPORT_DEBUG_API

#endif // _MW_DEBUG_STREAM_HPP_
